/*	
**    Copyright (C) 1992  Ronin Consulting, Inc.
**
**    This program is free software; you can redistribute it and/or modify
**    it under the terms of the GNU General Public License as published by
**    the Free Software Foundation; version 1.
**
**    This program is distributed in the hope that it will be useful,
**    but WITHOUT ANY WARRANTY; without even the implied warranty of
**    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**    GNU General Public License for more details.
**
**    You should have received a copy of the GNU General Public License
**    along with this program; if not, write to the Free Software
**    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#import <stdio.h>
#import "Controller.h"
#import "Defaults.h"
#import "StringStorage.h"
#import "TokenString.h"
#import "EnhancedText.h"
#import "EnhancedMatrix.h"
#import "Subprocess.h"
#import "version.h"
#import <appkit/appkit.h>
#import <libc.h>
#import <objc/NXStringTable.h>
#import <soundkit/soundkit.h>
#import "EnhancedApp.h"

@implementation Controller

- init
{
   [super init];

   [NXApp appDirectory];
   openPanel = [OpenPanel new];
   [openPanel allowMultipleFiles: NO];
   inFile = [[StringStorage alloc] init];
   savePanel = [SavePanel new];
   outFile = [[StringStorage alloc] init];

   strings = [[NXStringTable alloc] init];
   dataFormSwitches = [[NXStringTable alloc] init];
   dataSizeSwitches = [[NXStringTable alloc] init];
   inHotListStr = [[NXStringTable alloc] init];
   inHotListConfig = [[NXStringTable alloc] init];
   outHotListStr = [[NXStringTable alloc] init];
   outHotListConfig = [[NXStringTable alloc] init];

   theSound = [Sound new];
   [theSound setDelegate: self];

   return self;
}

- appDidInit: sender
{
   id buffer = [[StringStorage alloc] init];
   id dir = [[StringStorage alloc] init];

   defaults = [Defaults new];		     /* get access to applications defaults */

					     /* load the effects popup */
   if(![strings readFromFile: "English.lproj/effects.strings"])
       NXLogError("File effects is missiong from .app");

   [self loadPopUpList: effect with: strings from: 1];
   [strings empty];

					     /* load the filetype popups */
   if(![strings readFromFile: "English.lproj/filetype.strings"])
       NXLogError("File filetype is missiong from .app");

   [self loadPopUpList: inType with: strings from: 1];
   [self loadPopUpList: outType with: strings from: 2];
   [strings empty];

					     /* load the rates popups */
   if(![strings readFromFile: "English.lproj/rate.strings"])
       NXLogError("File rate missing\n");

   [self loadPopUpList: inRate with: strings from: 1];
   [[inRate target] setTarget: self];
   [[inRate target] setAction: @selector(copyValue:)];

   [self loadPopUpList: outRate with: strings from: 2];
   [[outRate target] setTarget: self];
   [[outRate target] setAction: @selector(copyValue:)];
   [outRateText setDoubleValue: atof([outRate title])];
   
   [strings empty];
					     /* load the format popups */
   if(![strings readFromFile: "English.lproj/dataFormat.strings"])
       NXLogError("File dataFormat missing\n");

   [self loadPopUpList: inDataForm with: strings from: 1];
   [self loadPopUpList: outDataForm with: strings from: 2];
   [strings empty];

					     /* laod the format swithes */
   if(![dataFormSwitches readFromFile: "English.lproj/dataFormatSwitches.strings"])
       NXLogError("File dataFormatSwitches missing\n");

					     /* load data size popups */
   if(![strings readFromFile: "English.lproj/dataSize.strings"])
       NXLogError("File dataSize missing\n");

   [self loadPopUpList: inDataSize with: strings from: 1];
   [self loadPopUpList: outDataSize with: strings from: 2];
   [strings empty];

					     /* laod the size swithes */
   if(![dataSizeSwitches readFromFile: "English.lproj/dataSizeSwitches.strings"])
       NXLogError("File dataSizeSwitches missing\n");

					     /* load data size popups */
   if(![strings readFromFile: "English.lproj/channel.strings"])
       NXLogError("File channel missing\n");

   [self loadPopUpList: inChannels with: strings from: 1];
   [self loadPopUpList: outChannels with: strings from: 2];
   [strings empty];

   [dir setStringValue: [defaults get: "StoreDirectory"]];

   if(!*[dir stringValue])
       [dir setStringValue: "English.lproj/"];
   else
       [dir appendStringValue: "/"];

   [buffer setStringValue: [dir stringValue]];
   [buffer appendStringValue: "inHotList.strings"];
					     /* load in hot list */
   if(![inHotListStr readFromFile: [buffer stringValue]])
       NXLogError("File inHotList missing.\n");

   [self loadPopUpList: inHotList with: inHotListStr from: 1];
   [[inHotList target] setTarget: self];
   [[inHotList target] setAction: @selector(loadFromHotList:)];

   [buffer setStringValue: [dir stringValue]];
   [buffer appendStringValue: "inHotListConfig.strings"];
					     /* load in hot list configs */
   if(![inHotListConfig readFromFile: [buffer stringValue]])
       NXLogError("File inHotListConfig missing.\n");


   [buffer setStringValue: [dir stringValue]];
   [buffer appendStringValue: "outHotList.strings"];
					     /* load out hot list */
   if(![outHotListStr readFromFile: [buffer stringValue]])
       NXLogError("File outHotList missing.\n");

   [self loadPopUpList: outHotList with: outHotListStr from: 1];
   [[outHotList target] setTarget: self];
   [[outHotList target] setAction: @selector(loadFromHotList:)];

   [buffer setStringValue: [dir stringValue]];
   [buffer appendStringValue: "outHotListConfig.strings"];
					     /* load out hot list configs */
   if(![outHotListConfig readFromFile: [buffer stringValue]])
       NXLogError("File outHotListConfig missing.\n");

   [self loadFromHotList: [[inHotList target] itemList]];
   [self loadFromHotList: [[outHotList target] itemList]];

   [panel makeKeyAndOrderFront: self];
   [dialog appendString: "Ready...\n"];

   return self;
}

- convert:sender
{
   char cmd[1024];

   [convert setTitle: "STOP"];
   NXPing();

   if(![convert state])			     /* already doing a conversion - stop it */
   {
      [subprocess terminate: self];
      [convert setTitle: "Do Conversion"];
      return self;
   }
   
   if (![[sender window] makeFirstResponder: [sender window]])
   {
      [dialog appendString: "Invalid entry.\n"];
      [convert setState: 0];
      [convert setTitle: "Do Conversion"];
      return self;
   }
   
   [[sender window] endEditingFor:nil];

   if(!*[inFile stringValue])
   {
      [dialog appendString: "No input File is set.\n"];
      [convert setState: 0];
      [convert setTitle: "Do Conversion"];
      return self;
   }


   if(!*[outFile stringValue])
   {
      char filename[strlen([defaults get: "TmpDirectory"]) + 15];

      sprintf(filename,"%s/GISOXXXXXX", [defaults get: "TmpDirectory"]);
      [outFile setStringValue: mktemp(filename)];
      [outFile appendStringValue: ".snd"];
   }

   /* 
    * Build up the command line.
    */
   strcpy(cmd, "sox -V ");

   if([volume doubleValue] != 1.0)
       sprintf(cmd + strlen(cmd), "-v %6.2f ", [volume doubleValue]);

   if([[inType target] indexOfItem: [inType title]])
       sprintf(cmd + strlen(cmd), "-t %s ", [inType title]);

   if([inRateText doubleValue])
       sprintf(cmd + strlen(cmd), "-r %10.3f ", [inRateText doubleValue]);

   if([[inDataForm target] indexOfItem: [inDataForm title]])
       sprintf(cmd + strlen(cmd), "%s ", 
	       [dataFormSwitches valueForStringKey: [inDataForm title]]);

   if([[inDataSize target] indexOfItem: [inDataSize title]])
       sprintf(cmd + strlen(cmd), "%s ",
	       [dataSizeSwitches valueForStringKey: [inDataSize title]]);

   if([[inChannels target] indexOfItem: [inChannels title]])
       sprintf(cmd + strlen(cmd), "-c %s ", [inChannels title]);

   if([swapBytes state])
       strcat(cmd, "-x ");

   sprintf(cmd + strlen(cmd), "%s -t %s -r %10.3f %s %s -c %s %s ",
	   [inFile stringValue],
	   [outType title],
	   [outRateText doubleValue],
	   [dataFormSwitches valueForStringKey: [outDataForm title]],
	   [dataSizeSwitches valueForStringKey: [outDataSize title]],
	   [outChannels title],
	   [outFile stringValue]);

   if([[effect target] indexOfItem: [effect title]])
       sprintf(cmd + strlen(cmd), "%s %s ", 
	       [effect title], [effectArgText stringValue]);

   [dialog appendString: cmd];
   [dialog appendString: "\n"];

   subprocess = [[Subprocess alloc] init: cmd withDelegate: self];
   return self;
}

- openFile:sender
{
   static int first = 1;

   if (first)
   {
      [openPanel runModalForDirectory: "~/" file: ""];
      first = 0;
   }
   else
       [openPanel runModalForDirectory: "" file: ""];

   [inFile setStringValue: [openPanel filename]];
   if(*[inFile stringValue])
   {
      [dialog appendString: [inFile stringValue]];
      [dialog appendString: " used as input\n"];
      [outFile setStringValue: ""];
   }
   
   return self;
}

- saveFile:sender
{
   static int first = 1;
   id tempFile = [[StringStorage alloc] init];

   if(!*[outFile stringValue])
   {
      [dialog appendString: "No sound created.\n"];
      [tempFile free];
      return self;
   }
   
   if(first)
   {
      [savePanel runModalForDirectory: "~/" file: ""];
      first = 0;
   }
   else
      [savePanel runModalForDirectory: "" file: ""];

   [tempFile setStringValue: [savePanel filename]];
   if(*[tempFile stringValue])
   {
      char cmd[128];

      [dialog appendString: [savePanel filename]];
      [dialog appendString: " used as output\n"];
      sprintf(cmd, "cp \"%s\" \"%s\" ", [outFile stringValue], [tempFile stringValue]);
      system(cmd);
      [outFile setStringValue: [tempFile stringValue]];
   }

   [tempFile free];
   return self;
}

- saveToHotList: sender
{
   id buffer = [[StringStorage alloc] init];
   id file = [[StringStorage alloc] init];
   id dir = [[StringStorage alloc] init];
   char num[10],  *name;
   BOOL inHot;

   name = (char *)[entryName stringValue];

   if(!name || !*name)
   {
      NXRunAlertPanel([NXApp appName], "Entry must have a name.", 
		       NULL, NULL, NULL);
      [dir free];
      [buffer free];
      [file free];
      return self;
   }
   

   [dir setStringValue: [defaults get: "StoreDirectory"]];

   
   if(!*[dir stringValue])
       NXRunAlertPanel([NXApp appName], "No directory to store the hot lists - see preferences.", 
		       NULL, NULL, NULL);
   else
   {
      inHot = [whichRadio state];
      
      [buffer setStringValue: [(inHot?inType:outType) title]];
      [buffer appendStringValue: "|"];
      [buffer appendStringValue: [(inHot?inRateText:outRateText) stringValue]];
      [buffer appendStringValue: "|"];
      [buffer appendStringValue: [(inHot?inDataForm:outDataForm) title]];
      [buffer appendStringValue: "|"];
      [buffer appendStringValue: [(inHot?inDataSize:outDataSize) title]];
      [buffer appendStringValue: "|"];
      [buffer appendStringValue: [(inHot?inChannels:outChannels) title]];
      
      sprintf(num,"%ud", [(inHot?inHotListStr: outHotListStr) count] + 1);
      
      [(inHot?inHotListStr:outHotListStr) insertKey: num value: name];
      [(inHot?inHotListConfig:outHotListConfig) insertKey: name value: (char *)[buffer stringValue]];
      
      if(inHot)
      {
	 [file setStringValue: [dir stringValue]];
	 [file appendStringValue: "/inHotList.strings"];
	 [inHotListStr writeToFile: [file stringValue]];
	 [file setStringValue: [dir stringValue]];
	 [file appendStringValue: "/inHotListConfig.strings"];
	 [inHotListConfig writeToFile: [file stringValue]];
	 [[inHotList target] addItem: [entryName stringValue]];
      }
      else
      {
	 [file setStringValue: [dir stringValue]];
	 [file appendStringValue: "/outHotList.strings"];
	 [outHotListStr writeToFile: [file stringValue]];
	 [file setStringValue: [dir stringValue]];
	 [file appendStringValue: "/outHotListConfig.strings"];
	 [outHotListConfig writeToFile: [file stringValue]];
	 [[outHotList target] addItem: [entryName stringValue]];
      }
   }
   
   [dir free];
   [buffer free];
   [file free];
   [[sender window] performClose: self];
   return self;
}

- stopPlay: sender
{
   [theSound stop];
   return self;
}

- playIt: sender
{

   if(*[outFile stringValue])
   {
      [theSound readSoundfile: (char *)[outFile stringValue]];
      [theSound play];
   }
   else
   {
      [dialog appendString: "No sound has been created.\n"];
      [playButton setState: 0];
   }

   return self;
}

- copyValue: sender
{
   if(sender == [[inRate target] itemList])
       [inRateText setDoubleValue: atof([[sender selectedCell] title])];
   else
       [outRateText setDoubleValue: atof([[sender selectedCell] title])];

   return self;
}


- willPlay: sender
{
   [soundMeter run: self];
   return self;
}

- didPlay: sender
{
   [playButton setState: 0];
   [soundMeter stop: self];
   return self;
}

- hadError: sender
{
   [dialog appendString: "Error playing sound...\n"];
   return self;
}

- setDialog: anObject
{
   dialog = [anObject docView];
   return self;
}

- setSoundMeter: anObject
{
   soundMeter = anObject;
   [soundMeter setSound: theSound];
   
   return self;
}

- loadFromHotList: sender
{
   const char *title;
   id tokens = [[TokenString alloc] init];
   BOOL inHot;

   [tokens setSeparator: '|'];

   inHot = ((sender == [[inHotList target] itemList])? YES : NO);

   [tokens setStringValue: [(inHot?inHotListConfig:outHotListConfig) 
			valueForStringKey: [[sender selectedCell] title]]];

   if(!(title = [tokens popToken]))
   {
      [dialog appendString: "Malformed hot list entry\n"];
      return self;
   }

   [(inHot?inType:outType) setTitle: title];

   if(!(title = [tokens popToken]))
   {
      [dialog appendString: "Malformed hot list entry\n"];
      return self;
   }
   
   [(inHot?inRateText:outRateText) setStringValue: title];

   if(!(title = [tokens popToken]))
   {
      [dialog appendString: "Malformed hot list entry\n"];
      return self;
   }
   
   [(inHot?inDataForm:outDataForm) setTitle: title];

   if(!(title = [tokens popToken]))
   {
      [dialog appendString: "Malformed hot list entry\n"];
      return self;
   }
   
   [(inHot?inDataSize:outDataSize) setTitle: title];

   if(!(title = [tokens popToken]))
   {
      [dialog appendString: "Malformed hot list entry\n"];
      return self;
   }
   
   [(inHot?inChannels:outChannels) setTitle: title];

   [tokens free];

   return self;
}

- setVersionNo: anObject		     /* set the version number on the info panel */
{
   [anObject setStringValue: versionNumber];
   return self;
}

- (int) loadPopUpList: button with: stringTable from: (int) x
{
   char key[15];
   const char *value;
   id list = [button target];		     /* get the popUpList */

   while(1)
   {
      sprintf(key, "%d", x++);
      value = [stringTable valueForStringKey: key];

      if(!value)
	  break;

      [list addItem: value];
   }

   [list removeItem: [button title]];	     /* clear out the .nibs item */
   [button setTitle: [[[list itemList] cellAt: 0:0] title]];
   [[list itemList] selectCellAt: 0 : 0];
   return --x;
}

- cleanKill: sender			     /* clean temporary files before we go */
{
   char cmd[strlen([defaults get: "TmpDirectory"]) + 50];

   sprintf(cmd, "rm -f %s/GISO[0-9][0-9][0-9][0-9][0-9][0-9].snd", [defaults get: "TmpDirectory"]);
   system(cmd);
   return [NXApp terminate: self];
}

- (BOOL)appAcceptsAnotherFile:sender
{
   return YES;
}

- (int)app:sender openFile:(const char *)filename type:(const char *)aType
{
   [inFile setStringValue: filename];
   if(*[inFile stringValue])
   {
      [dialog appendString: [inFile stringValue]];
      [dialog appendString: " used as input\n"];
      [outFile setStringValue: ""];
      return YES;
   }

   return NO;
}

- subprocess:sender done:(int)exitStatus
{
   if(exitStatus)
       [dialog appendString: "ABORTED\n"];
   else
       [dialog appendString: "Done.\n"];

   [subprocess free];
   [convert setState: 0];
   [convert setTitle: "Do Conversion"];
   return self;
}

- subprocess:sender output:(char *)buffer
{
   [dialog appendString: buffer];
   [dialog appendString: "\n"];
   return self;
}

- subprocess:sender stderrOutput:(char *)buffer
{
   [dialog appendString: buffer];
   [dialog appendString: "\n"];
   return self;
}

- subprocess:sender error:(const char *)errorString
{
   [dialog appendString: errorString];
   [dialog appendString: "\n"];
   return self;
}

@end
